package main

import (
	"crypto/sha256"
	"time"
	"encoding/binary"
	"bytes"
	"log"
)

const genesisInfo = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"

type Block struct {
	//版本号
	Version uint64

	//前区块哈希值
	PreHash []byte

	//梅克尔根(就是一个哈希值，v4版本介绍）
	MerKleRoot []byte

	//时间戳
	TimeStamp uint64

	//难度值(调整比特币挖矿的难度)
	Difficulty uint64

	//随机数，这就是挖矿时所要寻找的数
	Nonce uint64

	//当前区块哈希值(为了方便实现，所以将区块的哈希值放到了区块中)
	Hash []byte

	//区块数据
	Data []byte
}

//产生创世块
func GenesisBlock(data string, prevBlockHash []byte) *Block {
	return NewBlock(data, prevBlockHash)
}

func NewBlock(data string, prevBlockHash []byte) *Block {
	block := Block{
		Version:    00,
		PreHash:    prevBlockHash,
		MerKleRoot: []byte{}, //先填为空，v4版本再详解
		TimeStamp:  uint64(time.Now().Unix()),
		Difficulty: 100,
		Nonce:      100,
		//Hash:    []byte{}, //先填为空，后面再进行计算
		Data: []byte(data)}

	//提供一个设置哈希的方法
	block.SetHash()
	return &block
}

//问题：
//1. 类型转换  uint64 -> []byte
//2. 使用bytes.Join()将数据拼接起来

func (block *Block) SetHash() {
	var blockByteInfo []byte //存储拼接好的数据，最后作为sha256函数的参数
	//1. 拼接当前区块的数据
	/*
	blockByteInfo = append(blockByteInfo, block.PreHash...)
	blockByteInfo = append(blockByteInfo, block.Data...)
	blockByteInfo = append(blockByteInfo, block.MerKleRoot...)
	blockByteInfo = append(blockByteInfo, uint64ToByte(block.Version)...)
	blockByteInfo = append(blockByteInfo, uint64ToByte(block.TimeStamp)...)
	blockByteInfo = append(blockByteInfo, uint64ToByte(block.Difficulty)...)
	blockByteInfo = append(blockByteInfo, uint64ToByte(block.Nonce)...)
	*/

	tmp := [][]byte{
		block.PreHash,
		block.Data,
		block.MerKleRoot,
		uint64ToByte(block.Version),
		uint64ToByte(block.TimeStamp),
		uint64ToByte(block.Difficulty),
		uint64ToByte(block.Nonce)}

	blockByteInfo = bytes.Join(tmp, []byte(""))

	//2. 对数据进行哈希处理：sha256
	//func Sum(data []byte) [Size]byte {
	hash := sha256.Sum256(blockByteInfo) //传入参数时切片， 返回值是一个32位的数组

	//3. 把哈希添加到我们区块Hash字段
	block.Hash = hash[:]
}

func uint64ToByte(num uint64) []byte {
	//func Write(w io.Writer, order ByteOrder, data interface{}) error {
	var buffer bytes.Buffer
	//将数据以二进制形式保存到buffer中
	err := binary.Write(&buffer, binary.BigEndian, num)
	if err != nil {
		log.Panic(err)
	}

	return buffer.Bytes()
}
